import { Suspense, useState } from "react";
import { graphql, useLazyLoadQuery } from "react-relay";
import { css } from "@emotion/react";

import {
  Button,
  CredentialField,
  CredentialInput,
  Dialog,
  DialogTrigger,
  ExternalLink,
  Flex,
  Form,
  Heading,
  Icon,
  Icons,
  Label,
  Popover,
  Skeleton,
  Text,
  ToggleButton,
  ToggleButtonGroup,
  View,
} from "@phoenix/components";
import { GenerativeProviderIcon } from "@phoenix/components/generative/GenerativeProviderIcon";
import { ProviderToCredentialsConfigMap } from "@phoenix/constants/generativeConstants";
import { useCredentialsContext } from "@phoenix/contexts/CredentialsContext";
import { usePlaygroundContext } from "@phoenix/contexts/PlaygroundContext";
import {
  getProviderName,
  isModelProvider,
} from "@phoenix/utils/generativeUtils";

import type { PlaygroundCredentialsDropdownQuery } from "./__generated__/PlaygroundCredentialsDropdownQuery.graphql";

export function PlaygroundCredentialsDropdown() {
  const currentProviders = usePlaygroundContext((state) =>
    Array.from(
      new Set(state.instances.map((instance) => instance.model.provider))
    )
  );
  const isRunning = usePlaygroundContext((state) =>
    state.instances.some((instance) => instance.activeRunId != null)
  );

  const [credentialView, setCredentialView] = useState<"local" | "server">(
    "local"
  );

  return (
    <div
      css={css`
        .ac-dropdown-button {
          min-width: 0px;
        }
      `}
    >
      <DialogTrigger>
        <Button
          size="S"
          isDisabled={isRunning}
          trailingVisual={<Icon svg={<Icons.ChevronDown />} />}
        >
          API Keys
        </Button>
        <Popover style={{ width: "500px" }}>
          <Dialog>
            {({ close }) => (
              <View padding="size-200">
                <Form
                  onSubmit={(e) => {
                    e.preventDefault();
                    close();
                  }}
                >
                  <Flex
                    direction="row"
                    justifyContent="space-between"
                    alignItems="center"
                  >
                    <Heading level={2} weight="heavy">
                      API Keys
                    </Heading>
                    <ToggleButtonGroup
                      selectedKeys={[credentialView]}
                      size="S"
                      aria-label="Credential Source"
                      onSelectionChange={(v) => {
                        if (v.size === 0) {
                          return;
                        }
                        const view = v.keys().next().value;
                        if (view === "local" || view === "server") {
                          setCredentialView(view);
                        }
                      }}
                    >
                      <ToggleButton aria-label="Local" id="local">
                        Local
                      </ToggleButton>
                      <ToggleButton aria-label="Server" id="server">
                        Server
                      </ToggleButton>
                    </ToggleButtonGroup>
                  </Flex>
                  {credentialView === "local" ? (
                    <LocalCredentialsView providers={currentProviders} />
                  ) : (
                    <Suspense fallback={<ServerCredentialsSkeleton />}>
                      <ServerCredentialsView providers={currentProviders} />
                    </Suspense>
                  )}
                  <View paddingTop="size-100">
                    <Flex
                      direction="row"
                      gap="size-100"
                      width="100%"
                      justifyContent="end"
                    >
                      <ExternalLink href="/settings/providers">
                        View all AI provider configurations
                      </ExternalLink>
                    </Flex>
                  </View>
                </Form>
              </View>
            )}
          </Dialog>
        </Popover>
      </DialogTrigger>
    </div>
  );
}

function ServerCredentialsSkeleton() {
  return (
    <View paddingY="size-100">
      <Skeleton width="100%" height={20} animation="wave" />
      <View paddingTop="size-100">
        <Flex direction="column" gap="size-100">
          <View paddingY="size-50">
            <Flex direction="row" gap="size-100" alignItems="center">
              <Skeleton width={24} height={24} borderRadius="circle" />
              <Skeleton width={120} height={20} animation="wave" />
            </Flex>
            <View paddingTop="size-100">
              <Flex direction="column" gap="size-50">
                <Skeleton width="80%" height={16} animation="wave" />
                <Skeleton width="70%" height={16} animation="wave" />
              </Flex>
            </View>
          </View>
        </Flex>
      </View>
    </View>
  );
}

function LocalCredentialsView({ providers }: { providers: ModelProvider[] }) {
  return (
    <>
      <View paddingY="size-50">
        <Text color="text-700" size="XS">
          Local API keys are stored in your browser and are not shared with
          other users.
        </Text>
      </View>
      <Flex direction="column" gap="size-100">
        {providers.map((provider) => {
          const providerHasNoCredentials =
            !ProviderToCredentialsConfigMap[provider].length;
          if (providerHasNoCredentials) {
            // Do not show the credential field
            return null;
          }
          return (
            <View key={provider} paddingY="size-50">
              <Flex direction="row" gap="size-100" alignItems="center">
                <GenerativeProviderIcon provider={provider} />
                <Heading level={3} weight="heavy">
                  {getProviderName(provider)}
                </Heading>
              </Flex>
              <View paddingBottom="size-100" paddingTop="size-100">
                <ProviderCredentials provider={provider} />
              </View>
            </View>
          );
        })}
      </Flex>
    </>
  );
}

function ServerCredentialsView({ providers }: { providers: ModelProvider[] }) {
  const data = useLazyLoadQuery<PlaygroundCredentialsDropdownQuery>(
    graphql`
      query PlaygroundCredentialsDropdownQuery {
        modelProviders {
          key
          credentialRequirements {
            envVarName
            isRequired
          }
          credentialsSet
        }
      }
    `,
    {},
    { fetchPolicy: "network-only" }
  );

  // Create a map of provider key to credentialsSet status
  const credentialsStatusMap = new Map<ModelProvider, boolean | undefined>();
  data.modelProviders.forEach((provider) => {
    if (isModelProvider(provider.key)) {
      credentialsStatusMap.set(provider.key, provider.credentialsSet);
    }
  });

  return (
    <View paddingY="size-100">
      <Text color="text-700" size="S">
        Server-side API keys are configured via environment variables and will
        be available to all users.
      </Text>
      <View paddingTop="size-100">
        <Flex direction="column" gap="size-100">
          {providers.map((provider) => {
            const credentialsConfig = ProviderToCredentialsConfigMap[provider];
            if (!credentialsConfig.length) {
              return null;
            }
            const credentialsSet = credentialsStatusMap.get(provider);
            return (
              <View key={provider} paddingY="size-50">
                <Flex
                  direction="row"
                  gap="size-100"
                  alignItems="center"
                  justifyContent="space-between"
                >
                  <Flex direction="row" gap="size-100" alignItems="center">
                    <GenerativeProviderIcon provider={provider} />
                    <Heading level={3} weight="heavy">
                      {getProviderName(provider)}
                    </Heading>
                  </Flex>
                  {credentialsSet ? (
                    <Flex direction="row" gap="size-50" alignItems="center">
                      <Text color="success" size="S">
                        Configured
                      </Text>
                      <Icon
                        color="success"
                        svg={<Icons.CheckmarkCircleOutline />}
                      />
                    </Flex>
                  ) : (
                    <Flex direction="row" gap="size-50" alignItems="center">
                      <Text color="text-700" size="S">
                        Not Configured
                      </Text>
                      <Icon svg={<Icons.MinusCircleOutline />} />
                    </Flex>
                  )}
                </Flex>
                <View paddingTop="size-100">
                  <Flex direction="column" gap="size-50">
                    {credentialsConfig.map((credentialConfig) => (
                      <Text
                        key={credentialConfig.envVarName}
                        color="text-600"
                        size="S"
                      >
                        • {credentialConfig.envVarName}
                        {credentialConfig.isRequired && (
                          <Text color="text-700" weight="heavy">
                            {" "}
                            (required)
                          </Text>
                        )}
                      </Text>
                    ))}
                  </Flex>
                </View>
              </View>
            );
          })}
        </Flex>
      </View>
    </View>
  );
}

function ProviderCredentials({ provider }: { provider: ModelProvider }) {
  const setCredential = useCredentialsContext((state) => state.setCredential);
  const credentialsConfig = ProviderToCredentialsConfigMap[provider];
  const credentials = useCredentialsContext((state) => state[provider]);
  const isRunning = usePlaygroundContext((state) =>
    state.instances.some((instance) => instance.activeRunId != null)
  );
  return (
    <View>
      {credentialsConfig.map((credentialConfig) => (
        <CredentialField
          size="S"
          key={credentialConfig.envVarName}
          isRequired={credentialConfig.isRequired}
          onChange={(value) => {
            setCredential({
              provider,
              envVarName: credentialConfig.envVarName,
              value,
            });
          }}
          value={credentials?.[credentialConfig.envVarName] ?? ""}
          isDisabled={isRunning}
        >
          <Label>{credentialConfig.envVarName}</Label>
          <CredentialInput />
          <Text slot="description">
            {`Alternatively, you can set the "${credentialConfig.envVarName}" environment variable on the server.`}
          </Text>
        </CredentialField>
      ))}
    </View>
  );
}
